import { SystemRoleEntity } from './../role/entities/role.entity';
import { Injectable } from '@nestjs/common';
import { CreateUserDto, DeleteUserDto, ListUserDto, ResetPwdDto, UpdateUserDto } from './dto/index.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { SystemUserEntity } from './entities/user.entity';
import { ClientInfoDto, LoginDto } from '../access/dto/index.dto';
import { ResultData } from 'src/common/utils/result';
import { In, Not, Repository } from 'typeorm';
import { SYS_DEL_FLAG, SYS_STATUS } from 'src/common/enum/status';
import { GenerateUUID, getNowDate, GetNowDateFormat } from 'src/common/utils';
import { JwtService } from '@nestjs/jwt';
import { CacheEnum } from 'src/common/enum/cache';
import { LOGIN_TOKEN_EXPIRESIN, saltOrRounds } from 'src/common/constant';
import { RedisService } from 'src/modules/redis/redis.service';
import { SYS_USER_TYPE } from 'src/common/enum/user';
import { SystemDeptEntity } from '../dept/entities/dept.entity';
import { SystemPostEntity } from '../post/entities/post.entity';
import * as bcrypt from 'bcrypt';
import { CacheService } from 'src/modules/cache/cache.service';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(SystemUserEntity)
    private readonly userRepo: Repository<SystemUserEntity>,
    @InjectRepository(SystemRoleEntity)
    private readonly roleRepo: Repository<SystemRoleEntity>,
    @InjectRepository(SystemDeptEntity)
    private readonly deptRepo: Repository<SystemDeptEntity>,
    @InjectRepository(SystemPostEntity)
    private readonly postRepo: Repository<SystemPostEntity>,

    private readonly jwtService: JwtService,
    private readonly redisService: RedisService,
    private readonly cacheService: CacheService,
  ) { }

  /**
   * 创建用户
   * @param createUserDto 
   */
  async create(createUserDto: CreateUserDto) {
    let { userName, password } = createUserDto;
    const entity = this.userRepo.createQueryBuilder('user');
    entity.where({
      userName
    });
    let data = await entity.getOne();
    if (data) {
      return ResultData.fail(500, '用户已存在');
    }
    createUserDto.password = await bcrypt.hash(password, saltOrRounds)
    const res = await this.userRepo.save({ ...createUserDto, userType: SYS_USER_TYPE.CUSTOM });
    return ResultData.ok();
  }
  /**
   * 更新
   */
  async update(updateInfo: UpdateUserDto) {
    let { id, password } = updateInfo;
    if (!id) {
      return ResultData.fail(500, "请指定更新用户");
    }
    if (password) {
      updateInfo.password = await bcrypt.hash(password, saltOrRounds)
    }
    const loginTime = GetNowDateFormat();
    delete updateInfo.password;
    delete (updateInfo as any).depts;
    delete (updateInfo as any).roles;
    delete (updateInfo as any).posts;

    let res = await this.userRepo.update({ id: updateInfo.id }, { ...updateInfo });
    this.cacheService.storeSet(`user_${updateInfo.id}_roleIds`, updateInfo.roleIds, LOGIN_TOKEN_EXPIRESIN)
    if (res.affected) {
      return ResultData.ok()
    } else {
      return ResultData.fail(500, '更新用户失败')
    }
  }

  /**
   * 删除
   */
  async remove(userInfo: DeleteUserDto) {
    const data = await this.userRepo.update(
      { id: In(userInfo.ids), userType: Not(SYS_USER_TYPE.SYS) },
      {
        delFlag: SYS_STATUS.STOP
      }
    )
    if (data.affected) {
      userInfo.ids.map(id => {
        this.cacheService.storeDel(`user_${id}_roleIds`)
      })
      return ResultData.ok(data);
    } else {
      return ResultData.fail(500, '删除用户失败')
    }
  }

  /**
   * 重置密码
   */
  async resetPassword(body: ResetPwdDto) {
    let { id, password } = body;
    if (!id) {
      return ResultData.fail(500, '请指定重密码用户')
    }
    if (!password) {
      return ResultData.fail(500, '请填写密码')
    }
    let userPassword = await bcrypt.hash(password, saltOrRounds)
    let res = await this.userRepo.update(
      {
        id: body.id
      },
      {
        password: userPassword
      }
    )
    if (res.affected) {
      return ResultData.ok()
    } else {
      return ResultData.fail(500, '不存在此用户')
    }
  }

  /**
   * 查询用户列表
   */
  async findList(query: ListUserDto) {

    const entity = this.userRepo.createQueryBuilder("user");
    entity.where('user.delFlag = :delFlag', { delFlag: SYS_DEL_FLAG.EXIST });
    //角色筛选
    if (query.roleId) {
      entity.where("JSON_CONTAINS(user.roleIds,:roleId)", { roleId: query.roleId });
    }
    //部门筛选
    if (query.deptId) {
      entity.where("JSON_CONTAINS(user.deptIds,:deptId)", { deptId: query.deptId });
    }
    //职位筛选
    if (query.postId) {
      entity.where("JSON_CONTAINS(user.postIds,:postId)", { postId: query.postId });
    }

    if (query.userName) {
      entity.andWhere(`user.userName LIKE "%${query.userName}%"`);
    }
    if (query.nickName) {
      entity.andWhere(`user.nickName LIKE "%${query.nickName}%"`);
    }
    if (query.phone) {
      entity.andWhere(`user.phone LIKE "%${query.phone}%"`)
    }
    if (query.status) {
      entity.andWhere('user.status =:status', { status: query.status })
    }
    if (query.startTime && query.endTime) {
      entity.andWhere("user.createTime BETWEEN :start AND :end", { start: query.startTime, end: query.endTime });
    }
    entity.orderBy("user.id", 'DESC')
    entity.skip(query.size * (query.page - 1)).take(query.size);
    // entity.leftJoinAndMapMany('user.roleIdList',SystemUserWithRoleEntity,'userRole','userRole.userId = user.userId');
    const users = await entity.getMany();
    let userList = [];
    userList = await Promise.all(users.map(async user => {
      const roleIds = user.roleIds;
      const deptIds = user.deptIds;
      const postIds = user.postIds;
      delete user.password;
      const roles = await this.roleRepo.findBy({ id: In(roleIds || []) });
      const depts = await this.deptRepo.findBy({ id: In(deptIds || []) });
      const posts = await this.postRepo.findBy({ id: In(postIds || []) });
      return {
        ...user,
        roles,
        depts,
        posts
      }
    }));

    //联查职位详情
    //联查部门详情
    const [list, total] = await entity.getManyAndCount();
    return ResultData.ok({
      list: userList,
      total,
      size: query.size,
      page: query.page
    })
  }

  /**
   * 登录
   */
  async login(user: LoginDto, clientInfo: ClientInfoDto) {
    const data = await this.userRepo.findOne({
      where: {
        userName: user.username,
      },
      select: ['id']
    });
    if (!data) {
      return ResultData.fail(500, '账号不存在');
    }
    const userInfo = await this.getUserInfo(data.id);
    const isMatch = await bcrypt.compare(user.password, userInfo.password);
    if (!isMatch) {
      return ResultData.fail(500, '密码错误');
    }
    if (userInfo.delFlag == SYS_DEL_FLAG.ABSENT) {
      return ResultData.fail(500, `您已被禁用，如需正常使用请联系管理员`);
    }
    if (userInfo.status == SYS_STATUS.STOP) {
      return ResultData.fail(500, `您已被停用，如需正常使用请联系管理员`);
    }
    const loginTime = getNowDate();
    const loginIp = clientInfo.ip;
    await this.userRepo.update({ id: userInfo.id }, { loginTime, loginIp });
    const uuid = GenerateUUID();
    const token = this.createToken({ uuid: uuid, id: userInfo.id });
    const permissions = ['*:*:*'];
    userInfo.loginTime = loginTime;
    const cacheData = {
      browser: clientInfo.browser,
      ipaddr: loginIp,
      loginLocation: clientInfo.location,
      loginTime: loginTime,
      os: clientInfo.os,
      permissions: permissions,
      token: uuid,
      user: userInfo,
      userId: userInfo.id,
      username: userInfo.userName,
    };
    //缓存的信息，如果存在，则剔除
    let catchToken = await this.cacheService.storeGet(`user_${userInfo.id}_token`) || await this.redisService.get(`user_${userInfo.id}_token`);
    if(catchToken){
      await this.redisService.del(`${CacheEnum.LOGIN_TOKEN_KEY}${catchToken}`);
    }
    this.cacheService.storeSet(`user_${userInfo.id}_roleIds`, userInfo.roleIds, LOGIN_TOKEN_EXPIRESIN)
    this.cacheService.storeSet(`user_${userInfo.id}_token`, uuid, LOGIN_TOKEN_EXPIRESIN)
    this.redisService.set(`${CacheEnum.LOGIN_TOKEN_KEY}${uuid}`, cacheData, LOGIN_TOKEN_EXPIRESIN);
    this.redisService.set(`user_${userInfo.id}_token`, uuid, LOGIN_TOKEN_EXPIRESIN);
    return ResultData.ok(
      {
        token,
        userInfo
      },
      '登录成功',
    );
  }

  /**
   * 获取用户信息
   */
  async getUserInfo(id: number) {
    const entity = this.userRepo.createQueryBuilder('user');
    entity.where({
      id,
      delFlag: SYS_DEL_FLAG.EXIST
    });
    const data = await entity.getOne();
    let { roleIds, deptIds, postIds } = data;
    const roles = await this.roleRepo.findBy({ id: In(roleIds || []) });
    const depts = await this.deptRepo.findBy({ id: In(deptIds || []) });
    const posts = await this.postRepo.findBy({ id: In(postIds || []) });
    return {
      ...data,
      roles,
      depts,
      posts
    };
  }
  /**
   * 从数据声明生成令牌
   *
   * @param payload 数据声明
   * @return 令牌
   */
  createToken(payload: { uuid: string; id: number }): string {
    const accessToken = this.jwtService.sign(payload);
    return accessToken;
  }

  /**
   * 从令牌中获取数据声明
   *
   * @param token 令牌
   * @return 数据声明
   */
  parseToken(token: string) {
    try {
      if (!token) return null;
      const payload = this.jwtService.verify(token.replace('Bearer ', ''));
      return payload;
    } catch (error) {
      return null;
    }
  }
}
